home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Ham Radio 2000 #2
/
Ham Radio 2000 - Volume 2.iso
/
HAMV2
/
TCP_IP
/
TNOS230S
/
POP2SERV.C
< prev
next >
Wrap
C/C++ Source or Header
|
1997-09-07
|
20KB
|
872 lines
/* POP2 Server state machine - see RFC 937
*
* also see other credits in popcli.c
* 10/89 Mike Stockett wa7dyx
* Modified 5/27/90 by Allen Gwinn, N5CKP, for later NOS releases.
* Added to NOS by PA0GRI 2/6/90 (and linted into "standard" C)
*
* Aug-Oct 92 Mike Bilow, N1BEE, mikebw@ids.net
* Extensive bug fixes, changed uses of Borland stat()
* to fsize() in order to fix intermittent crashes
*
* November 93 KO4KS/Brian A. Lantz
* Modified close_folder() for TNOS control files
*
*/
#include "global.h"
#ifdef POP2SERVER
#include <time.h>
#ifndef MSDOS
#include <sys/stat.h>
#endif
#include "mbuf.h"
#include "socket.h"
#include "smtp.h"
#include "mailbox.h"
#include "bm.h"
#include "stats.h"
#if !defined(_lint)
static char rcsid[] OPTIONAL = "$Id: pop2serv.c,v 1.19 1997/09/07 21:18:28 root Exp root $";
#endif
#undef DEBUG /* N1BEE */
/* ---------------- common server data structures ---------------- */
/* POP server control block */
struct pop_scb {
int socket; /* socket number for this connection */
char state; /* server state */
#define LSTN 0
#define AUTH 1
#define MBOX 2
#define ITEM 3
#define NEXT 4
#define DONE 5
char buf[TLINELEN+1]; /* input line buffer */
char count; /* line buffer length */
char username[32]; /* user/folder name */
FILE *wf; /* work folder file pointer */
int folder_len; /* number of msgs in current folder */
int msg_num; /* current msg number */
long msg_len; /* length of current msg */
int msg_status_size; /* size of the message status array */
long curpos; /* current msg's position in file */
long folder_file_size; /* length of the current folder file, in bytes */
long nextpos; /* next msg's position in file */
char folder_modified; /* mail folder contents modified flag */
int16 *msg_status; /* message status array pointer */
};
#define NULLSCB (struct pop_scb *)0
/* Response messages */
static char count_rsp[] = "#%d messages in this folder\n",
error_rsp[] = "- ERROR: %s\n",
greeting_msg[] = "+ POP2 %s\n",
/* length_rsp[] = "=%ld bytes in this message\n", */
length_rsp[] = "=%ld characters in Message #%d\n",
msg_line[] = "%s\n",
no_mail_rsp[] = "+ No mail, sorry\n",
no_more_rsp[] = "=%d No more messages in this folder\n",
signoff_msg[] = "+ Bye, thanks for calling\n";
static struct pop_scb *create_scb (void);
static void delete_scb (struct pop_scb *scb);
static void popserv (int s,void *unused,void *p);
static int poplogin (char *pass,char *username);
void state_error (struct pop_scb *,const char *);
void open_folder (struct pop_scb *);
void do_cleanup (struct pop_scb *);
void read_message (struct pop_scb *);
void retrieve_message (struct pop_scb *);
void deletemsg (struct pop_scb *,int);
void get_message (struct pop_scb *,int);
void print_message_length (struct pop_scb *);
void close_folder (struct pop_scb *);
#ifdef POP_FOLDERS
void select_folder (struct pop_scb *);
#endif
static void pop_sm (struct pop_scb *scb);
static int Spop = -1; /* prototype socket for service */
/* Start up POP receiver service */
int
pop2start (int argc, char **argv, void *p)
{
return (installserver (argc, argv, &Spop, "POP2 listener", IPPORT_POP2,
INADDR_ANY, "POP2 server", popserv, 4096, NULL));
}
/* Shutdown POP2 service (existing connections are allowed to finish) */
int
pop2stop (int argc, char **argv, void *p)
{
return (deleteserver (&Spop));
}
static void
popserv(s,unused,p)
int s;
void *unused;
void *p;
{
struct pop_scb *scb;
sockowner(s,Curproc); /* We own it now */
log(s,"open POP2");
if((scb = create_scb()) == NULLSCB) {
tputs(Nospace);
log(s,"close POP2 - no space");
close_s(s);
return;
}
#ifdef STATS_USE
STATS_adduse (1);
MiscUsers++;
#endif
scb->socket = s;
scb->state = AUTH;
usprintf(s,greeting_msg,Hostname);
loop: if ((scb->count = recvline(s,scb->buf,TLINELEN)) == -1){
/* He closed on us */
goto quit;
}
rip(scb->buf);
if (strlen(scb->buf) == 0) /* Ignore blank cmd lines */
goto loop;
#ifdef DEBUG
if(Mailtrace >= 3) {
tcmdprintf("POP2SERV(popserv): Processing line <%s>\n",scb->buf);
/* getch(); */
}
#endif /* DEBUG */
/* Convert lower, and mixed case commands to UPPER case - Ashok */
for(cp = scb->buf;*cp != ' ' && *cp != '\0';cp++)
*cp = toupper(*cp);
pop_sm(scb);
if (scb->state == DONE)
goto quit;
goto loop;
quit:
#ifdef STATS_USE
MiscUsers--;
#endif
log(scb->socket,"close POP2");
close_s(scb->socket);
delete_scb(scb);
}
/* Create control block, initialize */
static struct
pop_scb *create_scb()
{
register struct pop_scb *scb;
if((scb = (struct pop_scb *)callocw(1,sizeof (struct pop_scb))) == NULLSCB)
return NULLSCB;
scb->username[0] = '\0';
scb->msg_status = NULL;
scb->wf = NULL;
scb->count = scb->folder_file_size = scb->msg_num = 0;
scb->folder_modified = FALSE;
return scb;
}
/* Free resources, delete control block */
static void
delete_scb(scb)
register struct pop_scb *scb;
{
if (scb == NULLSCB)
return;
if (scb->wf != NULL)
fclose(scb->wf);
if (scb->msg_status != NULL)
free((char *)scb->msg_status);
free((char *)scb);
}
/* --------------------- start of POP server code ------------------------ */
#define BITS_PER_WORD 16
#define isSOM(x) ((strncmp(x,"From ",5) == 0)) /* Start Of Message */
/* Command string specifications */
static char ackd_cmd[] = "ACKD",
acks_cmd[] = "ACKS",
#ifdef POP_FOLDERS
fold_cmd[] = "FOLD ",
#endif
login_cmd[] = "HELO ",
nack_cmd[] = "NACK",
quit_cmd[] = "QUIT",
read_cmd[] = "READ",
retr_cmd[] = "RETR";
static void
pop_sm(scb)
struct pop_scb *scb;
{
char password[30];
if (scb == NULLSCB) /* be certain it is good -- wa6smn */
return;
switch(scb->state) {
case AUTH:
#ifdef DEBUG
if(Mailtrace >= 3)
tcmdprintf("POP2SERV(pop_sm): Entering case AUTH\n");
#endif /* DEBUG */
if (strncmp(scb->buf,login_cmd,strlen(login_cmd)) == 0){
sscanf(scb->buf,"HELO %31s %29s",scb->username,password);
#ifdef DEBUG
if(Mailtrace >= 3) {
tcmdprintf("POP2SERV(pop_sm): Processing USER %s PASS %s\n",scb->username,password);
tcmdprintf("POP2SERV(pop_sm): Calling poplogin() for %s:%s:\n",scb->username,password);
}
#endif /* DEBUG */
if (!poplogin(scb->username,password)) {
log(scb->socket,"POP2 access DENIED to %s",
scb->username);
state_error(scb,"Access DENIED!!");
return;
}
log(scb->socket,"POP2 access granted to %s",
scb->username);
open_folder(scb);
} else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0){
do_cleanup(scb);
} else
state_error(scb,"(AUTH) Expected HELO or QUIT command");
#ifdef DEBUG
if(Mailtrace >= 3)
tcmdprintf("POP2SERV(pop_sm): Leaving case AUTH\n");
#endif /* DEBUG */
break;
case MBOX:
if (strncmp(scb->buf,read_cmd,strlen(read_cmd)) == 0)
read_message(scb);
#ifdef POP_FOLDERS
else if (strncmp(scb->buf,fold_cmd,strlen(fold_cmd)) == 0)
select_folder(scb);
#endif
else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0) {
do_cleanup(scb);
} else
state_error(scb,
#ifdef POP_FOLDERS
"(MBOX) Expected FOLD, READ, or QUIT command");
#else
"(MBOX) Expected READ or QUIT command");
#endif
break;
case ITEM:
if (strncmp(scb->buf,read_cmd,strlen(read_cmd)) == 0)
read_message(scb);
#ifdef POP_FOLDERS
else if (strncmp(scb->buf,fold_cmd,strlen(fold_cmd)) == 0)
select_folder(scb);
#endif
else if (strncmp(scb->buf,retr_cmd,strlen(retr_cmd)) == 0)
retrieve_message(scb);
else if (strncmp(scb->buf,quit_cmd,strlen(quit_cmd)) == 0)
do_cleanup(scb);
else
state_error(scb,
#ifdef POP_FOLDERS
"(ITEM) Expected FOLD, READ, RETR, or QUIT command");
#else
"(ITEM) Expected READ, RETR, or QUIT command");
#endif
break;
case NEXT:
if (strncmp(scb->buf,ackd_cmd,strlen(ackd_cmd)) == 0){
/* ACKD processing */
deletemsg(scb,scb->msg_num);
scb->msg_num++;
get_message(scb,scb->msg_num);
} else if (strncmp(scb->buf,acks_cmd,strlen(acks_cmd)) == 0){
/* ACKS processing */
scb->msg_num++;
get_message(scb,scb->msg_num);
} else if (strncmp(scb->buf,nack_cmd,strlen(nack_cmd)) == 0){
/* NACK processing */
fseek(scb->wf,scb->curpos,SEEK_SET);
} else {
state_error(scb,"(NEXT) Expected ACKD, ACKS, or NACK command");
return;
}
print_message_length(scb);
scb->state = ITEM;
break;
case DONE:
do_cleanup(scb);
break;
default:
state_error(scb,"(TOP) State Error!!");
break;
}
#ifdef DEBUG
if(Mailtrace >= 3)
tcmdprintf("POP2SERV(pop_sm): Leaving state machine; state %u\n",scb->state);
#endif /* DEBUG */
}
static void
do_cleanup (struct pop_scb *scb)
{
void close_folder (struct pop_scb *);
close_folder(scb);
(void) usputs(scb->socket,signoff_msg);
scb->state = DONE;
}
static void
state_error (struct pop_scb *scb, const char *msg)
{
if(Mailtrace >= 2)
tcmdprintf(error_rsp,msg);
usprintf(scb->socket,error_rsp,msg);
scb->state = DONE;
}
#ifdef POP_FOLDERS
static void
select_folder(scb)
struct pop_scb *scb;
{
sscanf(scb->buf,"FOLD %s",scb->username);
if (scb->wf != NULL)
close_folder(scb);
open_folder(scb);
}
#endif
static void
close_folder (struct pop_scb *scb)
{
char folder_pathname[128];
char line[TLINELEN+1];
FILE *fd;
int deleted = FALSE;
int msg_no = 0;
struct stat folder_stat;
int newmail (struct pop_scb *);
int isdeleted (struct pop_scb *,int);
char *cp;
int firstIDline = 0, nextisBID = 0, k;
int public, lines = 0;
long last;
struct let lt;
if (scb->wf == NULL)
return;
if (!scb->folder_modified) {
/* no need to re-write the folder if we have not modified it */
fclose(scb->wf);
scb->wf = NULL;
free((char *)scb->msg_status);
scb->msg_status = NULL;
return;
}
sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
if (newmail(scb)) {
/* copy new mail into the work file and save the
message count for later */
if ((fd = fopen(folder_pathname,"r")) == NULL) {
state_error(scb,"Unable to add new mail to folder");
return;
}
fseek(scb->wf,0,SEEK_END);
fseek(fd,scb->folder_file_size,SEEK_SET);
while (!feof(fd)) {
fgets(line,TLINELEN,fd);
fputs(line,scb->wf);
}
fclose(fd);
}
/* now create the updated mail folder */
if ((fd = fopen(folder_pathname,"w")) == NULL){
state_error(scb,"Unable to update mail folder");
return;
}
rewind(scb->wf);
sprintf(line,"%s/control/%s.ctl",Mailspool,scb->username);
remove (line);
lt.start = last = 0;
while (!feof(scb->wf)){
fgets(line,TLINELEN,scb->wf);
if (feof(scb->wf))
continue;
kwait(NULL); /* give other processes time in long copy */
if (isSOM(line)){
lt.size = last - lt.start - lines;
if (lt.size && (deleted == FALSE))
updateCtl (scb->username, <);
lines = lt.status = 0;
lt.start = last;
firstIDline = 0;
msg_no++;
if (msg_no <= scb->folder_len)
deleted = isdeleted(scb,msg_no);
else
deleted = FALSE;
}
lines++;
if (!firstIDline && nextisBID && (cp=strstr(line,"AA")) != NULLCHAR) {
/*what follows is the message-number*/
lt.bid = atol(cp+2);
nextisBID = 0;
firstIDline = 1;
}
if (!strncmp ("Received: ", line, 10))
nextisBID = 1;
if (!strncmp ("Status: R", line, 9))
lt.status = BM_READ;
if (deleted)
continue;
fputs(line,fd);
last = ftell (fd);
}
lt.size = last - lt.start - lines;
if (deleted == FALSE)
updateCtl (scb->username, <);
fclose(fd);
/* trash the updated mail folder if it is empty */
/* if ((stat(folder_pathname,&folder_stat) == 0) && (folder_stat.st_size == 0)) */
if(fsize(folder_pathname) == 0L) /* N1BEE */
unlink(folder_pathname);
fclose(scb->wf);
scb->wf = NULL;
free((char *)scb->msg_status);
scb->msg_status = NULL;
}
static void
open_folder (struct pop_scb *scb)
{
char folder_pathname[64];
char line[TLINELEN+1];
FILE *fd;
struct stat folder_stat;
sprintf(folder_pathname,"%.45s/%.8s.txt",Mailspool,scb->username);
#ifdef DEBUG
if(Mailtrace >= 3) {
tcmdprintf("POP2SERV(open_folder): will open %s\n",folder_pathname);
}
#endif /* DEBUG */
scb->folder_len = 0;
scb->folder_file_size = 0;
/* Ordinarily, we would call stat() to find out if the file exists
and get its size at the same time. However, there is a bug in
Borland's stat() code which crashes DesqView and OS/2 (!) if
stat() is called on a file which does not exist. -- N1BEE
*/
/* if (stat(folder_pathname,&folder_stat)){ */
if((folder_stat.st_size = fsize(folder_pathname)) == -1L) { /* N1BEE */
#ifdef DEBUG
if(Mailtrace >= 3) {
tcmdprintf("POP2SERV(open_folder): folder not found (empty)\n");
}
#endif /* DEBUG */
(void) usputs(scb->socket,no_mail_rsp);
/* state remains AUTH, expecting HELO or QUIT */
return;
}
scb->folder_file_size = folder_stat.st_size;
if ((fd = fopen(folder_pathname,"r")) == NULL){
state_error(scb,"POP2SERV(open_folder): Unable to open mail folder");
return;
}
if ((scb->wf = tmpfile()) == NULL) {
state_error(scb,"POP2SERV(open_folder): Unable to create work folder");
return;
}
while(!feof(fd)) {
fgets(line,TLINELEN,fd);
/* scan for begining of a message */
if (isSOM(line))
scb->folder_len++;
/* now put the line in the work file */
fputs(line,scb->wf);
}
fclose(fd);
scb->msg_status_size = (scb->folder_len) / BITS_PER_WORD;
if ((((scb->folder_len) % BITS_PER_WORD) != 0) ||
(scb->msg_status_size == 0))
scb->msg_status_size++;
if ((scb->msg_status = (int16 *) callocw(scb->msg_status_size,
sizeof(int16))) == NULL) {
state_error(scb,"Unable to create message status array");
return;
}
usprintf(scb->socket,count_rsp,scb->folder_len);
scb->state = MBOX;
#ifdef DEBUG
if(Mailtrace >= 3)
tcmdprintf("POP2SERV: open_folder() completed successfully.\n");
#endif /* DEBUG */
}
static void
read_message (struct pop_scb *scb)
{
void get_message (struct pop_scb *,int);
void print_message_length (struct pop_scb *);
if (scb == NULLSCB) /* check for null -- wa6smn */
return;
if (scb->buf[sizeof(read_cmd) - 1] == ' ')
scb->msg_num = atoi(&(scb->buf[sizeof(read_cmd) - 1]));
else
scb->msg_num++;
get_message(scb,scb->msg_num);
print_message_length(scb);
scb->state = ITEM;
}
static void
retrieve_message (struct pop_scb *scb)
{
char line[TLINELEN+1];
long cnt;
if (scb == NULLSCB) /* check for null -- wa6smn */
return;
if (scb->msg_len == 0) {
state_error(scb,"Attempt to access a DELETED message!");
return;
}
cnt = scb->msg_len;
while(!feof(scb->wf) && (cnt > 0)) {
fgets(line,TLINELEN,scb->wf);
rip(line);
usprintf(scb->socket,msg_line,line);
cnt -= (strlen(line)+2); /* Compensate for CRLF */
}
scb->state = NEXT;
}
static void
get_message (struct pop_scb *scb, int msg_no)
{
char line[TLINELEN+1];
long ftell (FILE *);
int isdeleted (struct pop_scb *, int);
if (scb == NULLSCB) /* check for null -- wa6smn */
return;
scb->msg_len = 0;
if (msg_no > scb->folder_len) {
scb->curpos = 0;
scb->nextpos = 0;
return;
} else {
/* find the message and its length */
rewind(scb->wf);
while (!feof(scb->wf) && (msg_no > -1)) {
if (msg_no > 0)
scb->curpos = ftell(scb->wf);
fgets(line,TLINELEN,scb->wf);
rip(line);
if (isSOM(line))
msg_no--;
if (msg_no != 0)
continue;
scb->nextpos = ftell(scb->wf);
scb->msg_len += (strlen(line)+2); /* Add CRLF */
}
}
if (scb->msg_len > 0)
fseek(scb->wf,scb->curpos,SEEK_SET);
/* we need the pointers even if the message was deleted */
if (isdeleted(scb,scb->msg_num))
scb->msg_len = 0;
}
static int
poplogin(username,pass)
char *pass;
char *username;
{
char buf[80];
char *cp;
char *cp1;
FILE *fp;
#ifdef DEBUG
if(Mailtrace >= 3)
tcmdprintf("POP2SERV(poplogin): Opening POP users file %s\n",Popusers);
#endif /* DEBUG */
if((fp = fopen(Popusers,"r")) == NULLFILE) {
/* User file doesn't exist */
tprintf("POP2 users file %s not found\n",Popusers);
return(FALSE);
}
#ifdef DEBUG
if(Mailtrace >= 3)
tcmdprintf("POP2SERV(poplogin): Login request from %s:%s:\n",username,pass);
#endif /* DEBUG */
while(fgets(buf,sizeof(buf),fp),!feof(fp)) {
if(buf[0] == '#')
continue; /* Comment */
if((cp = strchr(buf,':')) == NULLCHAR)
/* Bogus entry */
continue;
*cp++ = '\0'; /* Now points to password */
if(strcmp(username,buf) == 0)
break; /* Found user name */
}
if(feof(fp)) {
#ifdef DEBUG
if(Mailtrace >= 3)
tcmdprintf("POP2SERV(poplogin): username not found in POPUSERS\n");
#endif /* DEBUG */
/* User name not found in file */
fclose(fp);
return(FALSE);
}
fclose(fp);
if ((cp1 = strchr(cp,':')) == NULLCHAR) {
#ifdef DEBUG
if(Mailtrace >= 3)
tcmdprintf("POP2SERV(poplogin): No second ':' in POPUSERS entry\n");
#endif /* DEBUG */
return(FALSE);
}
*cp1 = '\0';
if(strcmp(cp,pass) != 0) {
#ifdef DEBUG
if(Mailtrace >= 3)
tcmdprintf("POP2SERV(poplogin): Wrong password (%s) from user %s, expecting %s\n",pass,username,cp);
#endif /* DEBUG */
/* Password required, but wrong one given */
return(FALSE);
}
/* whew! finally made it!! */
#ifdef DEBUG
if(Mailtrace >= 3)
tcmdprintf("POP2SERV(poplogin): %s authenticated\n",username);
#endif /* DEBUG */
return(TRUE);
}
static int
isdeleted (struct pop_scb *scb, int msg_no)
{
int16 mask = 1,offset;
msg_no--;
offset = msg_no / BITS_PER_WORD;
mask <<= msg_no % BITS_PER_WORD;
return (((scb->msg_status[offset]) & mask)? TRUE:FALSE);
}
static void
deletemsg (struct pop_scb *scb, int msg_no)
{
int16 mask = 1,offset;
if (scb == NULLSCB) /* check for null -- wa6smn */
return;
msg_no--;
offset = msg_no / BITS_PER_WORD;
mask <<= msg_no % BITS_PER_WORD;
scb->msg_status[offset] |= mask;
scb->folder_modified = TRUE;
}
static int
newmail (struct pop_scb *scb)
{
char folder_pathname[64];
struct stat folder_stat;
sprintf(folder_pathname,"%s/%s.txt",Mailspool,scb->username);
/* if (stat(folder_pathname,&folder_stat)) { */
if((folder_stat.st_size = fsize(folder_pathname)) == -1L) { /* N1BEE */
state_error(scb,"Unable to get old mail folder's status");
return(FALSE);
} else
return ((folder_stat.st_size > scb->folder_file_size)? TRUE:FALSE);
}
static void
print_message_length (struct pop_scb *scb)
{
char *print_control_string;
if (scb == NULLSCB) /* check for null -- wa6smn */
return;
if (scb->msg_len > 0)
print_control_string = length_rsp;
else if (scb->msg_num <= scb->folder_len)
print_control_string = length_rsp;
else
print_control_string = no_more_rsp;
(void)usprintf(scb->socket,print_control_string,scb->msg_len,scb->msg_num);
}
#endif